1. BMP280大气气压传感器模块实物图及原理图
!!!注意看这个下面这幅BMP280原理图,SDO引脚是否接地(连接GND),其次,看CSB引脚是否固定了高电平(硬链连接上拉电阻)




2. 四引脚的BMP280的I2C协议有什么特点?
2.1 4 Pin BMP280 仅支持I2C
具体看BMP280数据手册,现在这款BMP280大气气压传感器模块已经被厂家固定为4个引脚了,分别为VIN、GND、SCL、SDA。
1. 由原理图可以看出BMP280的SDO引脚连接的是GND 接地,所以我们的这块板子的I2C 7位地址为0x76,也就是7位地址为1110110 (0x76)。
2. 原理图中CSB通过上拉电阻,固定为了高电平,所以无法使用SPI通信协议。
如果无法预览,请点击 此处下载 BMP280数据手册 PDF 文件。
BMP280数据手册里写明:
Page28:5.2 I²C Interface
The 7-bit device address is 111011x. The 6 MSB bits are fixed. The last bit is changeable by SDO value and can be changed during operation. Connecting SDO to GND results in slave address 1110110 (0x76); connection it to VDDIO results in slave address 1110111 (0x77), which is the same as BMP180’s I²C address. The SDO pin cannot be left floating; if left floating, the I²C address will be undefined. The I²C interface uses the following pins:
- SCK: serial clock (SCL)
- SDI: data (SDA)
- SDO: Slave address LSB (GND = ‘0’, VDDIO = ‘1’) CSB must be connected to VDDIO to select I²C interface. SDI is bi-directional with open drain to GND: it must be externally connected to VDDIO via a pull up resistor. Refer to chapter 6 for connection instructions.
Page30:5.3 SPI interface
The SPI interface is compatible with SPI mode ‘00’ (CPOL = CPHA = ‘0’) and mode ‘11’ (CPOL = CPHA = ‘1’). The automatic selection between mode ‘00’ and ‘11’ is determined by the value of SCK after the CSB falling edge.
2.2 BMP280支持三种电源模式
Page15: 3.6 Power modes The BMP280 offers three power modes: sleep mode, forced mode and normal mode. These can be selected using the mode[1:0] bits in control register 0xF4. Table 10: mode settings BELOW
| mode[1:0] (CMD) | Mode |
|---|---|
| 00 | sleep mode |
| 01 and 10 | Forced mode (SoverEng used this) |
| 11 | Normal mode |
3. BMP280 数据手册摘要有哪些要了解的?
3.1 BMP280数据手册基本信息
- 传感器型号:BMP280
- 制造商:Bosch Sensortec
- 文档版本:1.14
- 发布日期:2015年5月5日
- 文档编号:BST-BMP280-DS001-11
3.2 BMP280的产品概述
BMP280是一款基于Piezo-resistive技术的数字气压传感器,专为移动应用设计,具有高精度、低功耗和小尺寸特性。作为BMP180的继任者,其在噪声水平、接口支持和封装尺寸上均有显著提升,适合电池供电设备如手机、GPS模块和手表等场景。
3.3 关键参数
| 参数 | 规格 |
|---|---|
| 压力范围 | 300~1100 hPa(等效海拔+9000~-500米)2页 |
| 封装 | 8引脚LGA金属盖,尺寸2.0×2.5 mm²,高度0.95 mm2页 |
| 精度 | - 相对精度:±0.12 hPa(等效±1米,950~1050 hPa @25°C) - 绝对精度:典型±1 hPa(950~1100 hPa,0~+40°C)2页 2页 |
| 温度系数偏移 | 1.5 Pa/K(等效12.6 cm/K,25~40°C @900 hPa)2页 |
| 接口 | I²C(最高3.4 MHz)、SPI(3/4线,最高10 MHz)2页 2页 |
| 功耗 | 2.7 µA @1 Hz采样率2页 |
| 工作温度范围 | -40~+85°C2页 |
| 环保特性 | RoHS合规、无卤素、MSL 12页 2页 |
3.4 与BMP180对比
| 参数 | BMP180 | BMP280 |
|---|---|---|
| 封装尺寸 | 3.6×3.8 mm² | 2.0×2.5 mm²(缩小63%) |
| 最小供电电压(VDD) | 1.80 V | 1.71 V |
| 功耗(@3 Pa噪声) | 12 µA | 2.7 µA |
| 噪声 | 3 Pa | 1.3 Pa |
| 接口 | 仅I²C | I²C + SPI(3/4线) |
| 测量模式 | 仅强制模式 | 强制/正常模式 |
| 滤波器 | 无 | 5种带宽选项 |
3.5 典型应用与目标设备
3.5.1 典型应用
- GPS导航增强(首次定位时间优化、航位推算、坡度检测)2页
- 室内导航(楼层检测、电梯检测)2页
- 户外导航、休闲运动设备2页
- 气象预测2页
- 医疗健康(如肺活量计)2页
- 垂直速度指示(上升/下降速度)2页
3.5.2 目标设备
手机、平板电脑、GPS设备、导航系统、便携式医疗设备、家用气象站、飞行玩具、手表等2页~2页
3.6 功能描述
3.6.1 工作模式
- 睡眠模式:默认模式,无测量,功耗最低16页
- 强制模式:单次测量后返回睡眠模式,适合低采样率场景16页(本节都是此模式)
- 正常模式:循环进行测量与待机,支持IIR滤波,适合需持续监测场景16页
3.6.2 过采样与滤波
- 过采样设置:压力(×1~×16)和温度(×1~×16)可独立配置,平衡精度与功耗10页
- IIR滤波器:5种系数(2/4/8/16),抑制短期压力波动(如关门噪声)10页
3.6.3 数据读取与补偿
- 数据输出:20位无符号压力/温度原始数据,需通过校准参数补偿19页
- 校准参数:存储于0x88~0xA1寄存器,包括温度(dig_T1~T3)和压力(dig_P1~P9)补偿系数21页
- 补偿公式:提供64位整数、32位整数和浮点实现,确保高精度转换21页 44页
3.7 BMP280 接口规范
3.7.1 支持 I²C接口
- 7位地址:0x76(SDO接地)或0x77(SDO接VDDIO)28页
- 支持标准/快速/高速模式,SDA/SCL需外部上拉电阻28页
3.7.2 SPI接口
- 支持4线(SDI/SDO/SCK/CSB)和3线(SDI复用)模式,SPI模式00/1130页
- 最高时钟频率10 MHz,CSB低电平激活34页
4. BMP280的AI 提示词有哪些要明确的?
现在我手头上有一块 BMP280 大气压传感器模块,它有 4 个引脚,分别是 VIN、GND、SCL 和 SDA。 我已经将这个 BMP280 传感器模块的引脚连接到了 ESP32-S3 开发板上,具体接线如下:
- SCL 引脚连接到 ESP32-S3 的 7 号引脚
- SDA 引脚连接到了 15 号引脚
我电脑目前使用的是 VS Code 加 ESP-IDF 插件进行编程,已经通过安装管理器安装好了 ESP-IDF V6.0 版本。
现在请你告诉我应该怎么写代码。要求如下: - 不要使用现成的 BMP280 库或组件
- 我需要从头开始写,用 I2C 驱动来写
- 我看了原理图以及BMP280 datasheet 数据手册,SDO接GND,CSB硬连接了上拉电阻, BMP280 只有 4 个引脚,所以它的 I2C 地址就是 0x76,你看一下是否正确
请直接给出代码。
5. I2C协议读取BMP280大气气压传感器数据完整流程

6. ESP32 s3 编写驱动读取BMP280 气压数据(ESP-IDF)
#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h" // FreeRTOS 实时操作系统头文件
#include "freertos/task.h" // FreeRTOS 任务管理(如 vTaskDelay 延时)
#include "driver/i2c_master.h" // ESP-IDF 的 I2C 主机驱动
#include "esp_log.h" // ESP-IDF 日志打印工具
static const char *TAG = "BMP280"; // 日志标签,打印时会显示 [BMP280]
/* =============================================
I2C 引脚与通信参数定义
BMP280 通过 I2C 总线与 ESP32 通信
============================================= */
#define I2C_MASTER_SCL_IO GPIO_NUM_7 // SCL(时钟线)连接的 GPIO 引脚
#define I2C_MASTER_SDA_IO GPIO_NUM_15 // SDA(数据线)连接的 GPIO 引脚
#define I2C_MASTER_FREQ_HZ 400000 // I2C 通信速率:400kHz(快速模式)
#define I2C_PORT I2C_NUM_0 // 使用 ESP32 的 I2C 控制器 0
#define BMP280_SENSOR_ADDR 0x76 // BMP280 的 I2C 地址(SDO 引脚接 GND 时为 0x76)
#define I2C_TIMEOUT_MS 1000 // I2C 通信超时时间:1000 毫秒
/* =============================================
BMP280 寄存器地址定义
寄存器是传感器内部的"存储格子",通过读写这些地址来控制传感器
============================================= */
#define BMP280_REG_CHIP_ID 0xD0 // 芯片 ID 寄存器,读出来应该是 0x58
#define BMP280_REG_RESET 0xE0 // 软复位寄存器,写入 0xB6 可重置传感器
#define BMP280_REG_STATUS 0xF3 // 状态寄存器,可查询传感器是否正在测量
#define BMP280_REG_CTRL_MEAS 0xF4 // 控制寄存器,设置采样精度和工作模式
#define BMP280_REG_CONFIG 0xF5 // 配置寄存器,设置滤波器和待机时间
#define BMP280_REG_PRESS_MSB 0xF7 // 气压数据起始寄存器(共 6 字节:气压3字节+温度3字节)
#define BMP280_REG_CALIB_START 0x88 // 校准参数起始寄存器(共 24 字节)
/* =============================================
数据类型别名定义
为了和 BMP280 手册中的命名保持一致
============================================= */
typedef int32_t BMP280_S32_t; // 有符号 32 位整数
typedef uint32_t BMP280_U32_t; // 无符号 32 位整数
typedef int64_t BMP280_S64_t; // 有符号 64 位整数(气压补偿需要更大范围)
/* =============================================
全局句柄变量
句柄是 ESP-IDF 用来管理硬件资源的"引用"
============================================= */
static i2c_master_bus_handle_t bus_handle; // I2C 总线句柄(代表整条 I2C 总线)
static i2c_master_dev_handle_t dev_handle; // I2C 设备句柄(代表总线上的 BMP280 这个设备)
/* =============================================
校准参数(出厂时烧录在传感器内部)
BMP280 的原始 ADC 数据不是真实的温度/气压值,
需要用这些校准参数进行数学补偿计算才能得到真实值
============================================= */
static uint16_t dig_T1; // 温度校准参数 1(无符号)
static int16_t dig_T2, dig_T3; // 温度校准参数 2、3(有符号)
static uint16_t dig_P1; // 气压校准参数 1(无符号)
static int16_t dig_P2, dig_P3, dig_P4, dig_P5; // 气压校准参数 2-5
static int16_t dig_P6, dig_P7, dig_P8, dig_P9; // 气压校准参数 6-9
static BMP280_S32_t t_fine; // 温度补偿的中间值,气压补偿也需要用到它
/* =============================================
底层 I2C 通信函数:写寄存器
向传感器的某个寄存器写入一个字节的数据
============================================= */
static esp_err_t bmp280_write_reg(uint8_t reg_addr, uint8_t data) {
// 把寄存器地址和数据打包成 2 字节,通过 I2C 发送给传感器
uint8_t write_buf[2] = {reg_addr, data};
return i2c_master_transmit(dev_handle, write_buf, sizeof(write_buf), I2C_TIMEOUT_MS);
}
/* =============================================
底层 I2C 通信函数:读寄存器
从传感器的某个寄存器开始,连续读取 len 个字节
============================================= */
static esp_err_t bmp280_read_reg(uint8_t reg_addr, uint8_t *data, size_t len) {
// 先发送要读取的寄存器地址,再接收传感器返回的数据
return i2c_master_transmit_receive(dev_handle, ®_addr, 1, data, len, I2C_TIMEOUT_MS);
}
/* =============================================
温度补偿算法(来自 BMP280 官方手册 3.11.3 节)
传感器 ADC 读出的是原始数字值,不是摄氏度。
这个函数用校准参数把原始值换算成真实温度。
输入:adc_T —— 传感器读出的原始温度 ADC 值(20位)
输出:返回温度值(单位:0.01°C,例如返回 2510 表示 25.10°C)
副作用:同时计算并保存 t_fine(供气压补偿使用)
============================================= */
static BMP280_S32_t bmp280_compensate_T_int32(BMP280_S32_t adc_T) {
BMP280_S32_t var1, var2, T;
// 第一项:线性补偿
var1 = ((((adc_T>>3) - ((BMP280_S32_t)dig_T1<<1))) * ((BMP280_S32_t)dig_T2)) >> 11;
// 第二项:二次补偿(修正非线性误差)
var2 = ( ((((adc_T>>4) - ((BMP280_S32_t)dig_T1)) * ((adc_T>>4) - ((BMP280_S32_t)dig_T1))) >> 12) * ((BMP280_S32_t)dig_T3) ) >> 14;
// t_fine 是温度的精细中间值,后续气压补偿也需要它
t_fine = var1 + var2;
// 最终温度值(单位 0.01°C)
T = (t_fine * 5 + 128) >> 8;
return T;
}
/* =============================================
气压补偿算法(来自 BMP280 官方手册 3.11.3 节)
同样,ADC 读出的气压原始值需要用校准参数换算。
注意:必须先调用温度补偿函数(更新 t_fine),再调用此函数!
输入:adc_P —— 传感器读出的原始气压 ADC 值(20位)
输出:返回气压值(单位:1/256 Pa,例如返回 25600000 表示 100000 Pa = 1000 hPa)
============================================= */
static BMP280_U32_t bmp280_compensate_P_int64(BMP280_S32_t adc_P) {
BMP280_S64_t var1, var2, p;
// 利用 t_fine 进行温度相关的气压修正
var1 = ((BMP280_S64_t)t_fine) - 128000;
var2 = var1 * var1 * (BMP280_S64_t)dig_P6;
var2 = var2 + ((var1*(BMP280_S64_t)dig_P5)<<17);
var2 = var2 + (((BMP280_S64_t)dig_P4)<<35);
var1 = ((var1 * var1 * (BMP280_S64_t)dig_P3)>>8) + ((var1 * (BMP280_S64_t)dig_P2)<<12);
var1 = (((((BMP280_S64_t)1)<<47)+var1))*((BMP280_S64_t)dig_P1)>>33;
if (var1 == 0) return 0; // 防止除以零(避免程序崩溃)
p = 1048576 - adc_P;
p = (((p<<31) - var2) * 3125) / var1;
var1 = (((BMP280_S64_t)dig_P9) * (p>>13) * (p>>13)) >> 25;
var2 = (((BMP280_S64_t)dig_P8) * p) >> 19;
// 最终气压值(单位:1/256 Pa)
p = ((p + var1 + var2) >> 8) + (((BMP280_S64_t)dig_P7)<<4);
return (BMP280_U32_t)p;
}
/* =============================================
海拔换算函数(国际标准大气压模型)
根据当前气压推算海拔高度。
公式:h = 44330 × (1 - (P/P0)^0.1903)
其中 P0 = 101325 Pa(海平面标准大气压)
输入:pressure_pa —— 当前气压(单位:Pa)
输出:返回估算海拔(单位:米)
注意:这只是估算值,实际海拔受天气影响
============================================= */
static float pressure_to_altitude(float pressure_pa) {
float sea_level_pa = 101325.0f; // 海平面标准大气压(Pa)
return 44330.0f * (1.0f - powf((pressure_pa / sea_level_pa), 0.1903f));
}
/* =============================================
单次测量函数(强制模式 Forced Mode)
BMP280 有三种工作模式:
- Sleep 模式:不测量,最省电
- Forced 模式:触发一次测量,完成后自动回到 Sleep
- Normal 模式:持续周期性测量
这里使用 Forced 模式:每次需要数据时手动触发一次测量。
输出参数:
temperature —— 温度(°C)
pressure —— 气压(Pa)
altitude —— 估算海拔(m)
============================================= */
static esp_err_t bmp280_force_measurement(float *temperature, float *pressure, float *altitude) {
// 步骤1:向控制寄存器写入 0x25,触发一次强制测量
// 0x25 = 0b00100101
// osrs_t = 001 → 温度过采样 x1(采1次样)
// osrs_p = 001 → 气压过采样 x1(采1次样)
// mode = 01 → Forced 模式(触发单次测量)
ESP_ERROR_CHECK(bmp280_write_reg(BMP280_REG_CTRL_MEAS, 0x25));
// 步骤2:等待测量完成
// 状态寄存器 0xF3 的 bit3(measuring 位)= 1 表示正在测量
// 循环读取状态,直到 bit3 变为 0(测量完成)或超时
uint8_t status;
int timeout = 100; // 最多等待 100 × 10ms = 1秒
do {
vTaskDelay(pdMS_TO_TICKS(10)); // 等待 10 毫秒再检查
bmp280_read_reg(BMP280_REG_STATUS, &status, 1);
} while ((status & 0x08) && --timeout); // 0x08 = bit3,检查 measuring 位
if (timeout == 0) return ESP_ERR_TIMEOUT; // 超时则返回错误
// 步骤3:突发读取 6 字节原始数据(从 0xF7 开始)
// 手册要求必须一次性连续读取全部 6 字节,保证数据一致性
// 数据布局:[气压高8位][气压中8位][气压低4位+4位填充]
// [温度高8位][温度中8位][温度低4位+4位填充]
uint8_t data[6];
ESP_ERROR_CHECK(bmp280_read_reg(BMP280_REG_PRESS_MSB, data, 6));
// 步骤4:将 3 字节拼接成 20 位 ADC 原始值
// 气压:data[0]是高8位,data[1]是中8位,data[2]高4位是低4位
int32_t adc_P = ((int32_t)data[0] << 12) | ((int32_t)data[1] << 4) | (data[2] >> 4);
// 温度:data[3]是高8位,data[4]是中8位,data[5]高4位是低4位
int32_t adc_T = ((int32_t)data[3] << 12) | ((int32_t)data[4] << 4) | (data[5] >> 4);
// 步骤5:用校准参数进行补偿计算,得到真实物理值
// 注意:必须先算温度(更新 t_fine),再算气压!
*temperature = bmp280_compensate_T_int32(adc_T) / 100.0f; // 单位转换:0.01°C → °C
*pressure = bmp280_compensate_P_int64(adc_P) / 256.0f; // 单位转换:1/256 Pa → Pa
*altitude = pressure_to_altitude(*pressure); // 由气压推算海拔
return ESP_OK;
}
/* =============================================
主函数 app_main()
ESP-IDF 程序的入口,相当于 Arduino 的 setup() + loop()
============================================= */
void app_main(void) {
ESP_LOGI(TAG, "BMP280 强制模式驱动启动");
// ---- 第1步:初始化 I2C 总线 ----
// 配置 I2C 总线参数(引脚、速率等)
i2c_master_bus_config_t bus_cfg = {
.clk_source = I2C_CLK_SRC_DEFAULT, // 使用默认时钟源
.i2c_port = I2C_PORT, // 使用 I2C 控制器 0
.scl_io_num = I2C_MASTER_SCL_IO, // SCL 引脚
.sda_io_num = I2C_MASTER_SDA_IO, // SDA 引脚
.glitch_ignore_cnt = 7, // 毛刺过滤(忽略小于7个时钟周期的干扰)
.flags.enable_internal_pullup = true, // 启用内部上拉电阻
};
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_cfg, &bus_handle)); // 创建 I2C 总线
// 在总线上注册 BMP280 设备
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7, // 7位 I2C 地址
.device_address = BMP280_SENSOR_ADDR, // BMP280 地址 0x76
.scl_speed_hz = I2C_MASTER_FREQ_HZ, // 通信速率 400kHz
};
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle)); // 添加设备
// ---- 第2步:验证芯片 ID ----
// 读取 0xD0 寄存器,BMP280 应返回 0x58
// 这一步确认连接的确实是 BMP280,而不是其他传感器
uint8_t chip_id;
ESP_ERROR_CHECK(bmp280_read_reg(BMP280_REG_CHIP_ID, &chip_id, 1));
if (chip_id != 0x58) {
ESP_LOGE(TAG, "BMP280 ID错误: 0x%02X", chip_id); // 打印错误并停止
vTaskDelete(NULL);
}
ESP_LOGI(TAG, "BMP280 ID验证通过");
// ---- 第3步:软复位 ----
// 向复位寄存器写入 0xB6,让传感器恢复出厂默认状态
// 复位后:Sleep 模式,过采样关闭,滤波器关闭
bmp280_write_reg(BMP280_REG_RESET, 0xB6);
vTaskDelay(pdMS_TO_TICKS(10)); // 等待 10ms 让复位完成
// ---- 第4步:读取出厂校准参数 ----
// 从 0x88 开始读取 24 字节,包含温度和气压的校准系数
// 每个参数由 2 字节(低字节在前)拼成 16 位整数
uint8_t calib[24];
ESP_ERROR_CHECK(bmp280_read_reg(BMP280_REG_CALIB_START, calib, 24));
dig_T1 = (uint16_t)(calib[1] << 8 | calib[0]); // 温度校准参数 T1
dig_T2 = (int16_t) (calib[3] << 8 | calib[2]); // 温度校准参数 T2
dig_T3 = (int16_t) (calib[5] << 8 | calib[4]); // 温度校准参数 T3
dig_P1 = (uint16_t)(calib[7] << 8 | calib[6]); // 气压校准参数 P1
dig_P2 = (int16_t) (calib[9] << 8 | calib[8]); // 气压校准参数 P2
// ... P3 到 P9 同理,依次从 calib 数组中读取
dig_P3 = (int16_t)(calib[11] << 8 | calib[10]);
dig_P4 = (int16_t)(calib[13] << 8 | calib[12]);
dig_P5 = (int16_t)(calib[15] << 8 | calib[14]);
dig_P6 = (int16_t)(calib[17] << 8 | calib[16]);
dig_P7 = (int16_t)(calib[19] << 8 | calib[18]);
dig_P8 = (int16_t)(calib[21] << 8 | calib[20]);
dig_P9 = (int16_t)(calib[23] << 8 | calib[22]);
ESP_LOGI(TAG, "校准参数读取完成");
// ---- 第5步:配置滤波器 ----
// 向配置寄存器(0xF5)写入 0x00
// 0x00 表示:IIR 滤波器关闭(Filter::Off),待机时间 0.5ms
// 注意:根据 BMP280 规格,CONFIG 寄存器的写入必须在 Sleep 模式下进行,
// 软复位后传感器默认处于 Sleep 模式,所以这里可以直接写入
bmp280_write_reg(BMP280_REG_CONFIG, 0x00);
// ---- 第6步:循环触发 5 次单次测量 ----
// 每次调用 bmp280_force_measurement() 触发一次 Forced 模式测量:
// 1. 写控制寄存器 → 传感器开始采样
// 2. 等待采样完成
// 3. 读取 6 字节原始数据
// 4. 用校准参数换算成真实温度、气压、海拔
float temp, press, altitude;
for (int i = 0; i < 5; i++) {
if (bmp280_force_measurement(&temp, &press, &altitude) == ESP_OK) {
// press 单位是 Pa,除以 100 转换为 hPa(百帕,气象常用单位)
// 例如:101325 Pa ÷ 100 = 1013.25 hPa(标准大气压)
ESP_LOGI(TAG, "温度: %.2f °C | 气压: %.2f hPa | 海拔: %.2f m",
temp, press / 100.0f, altitude);
}
vTaskDelay(pdMS_TO_TICKS(2000)); // 每次测量间隔 2 秒
}
ESP_LOGI(TAG, "测量完成");
// 程序结束,app_main 返回后 ESP-IDF 会自动删除此任务
}